package eval

import "showen7/inter"

func Cons(args []inter.Object, env *inter.Environment) inter.Object {
	if len(args) != 2 {
		return &Error{Message: "cons requires exactly 2 arguments"}
	}

	// Convert inter.Object to ds.Pair compatible types
	var car, cdr any

	// Handle car
	switch args[0].Type() {
	case inter.NUMBER_OBJ:
		car = args[0].(*inter.NumberObject).Value
	case inter.BOOLEAN_OBJ:
		car = args[0].(*inter.BooleanObject).Value
	case inter.CHARACTER_OBJ:
		car = args[0].(*inter.CharacterObject).Value
	default:
		car = args[0]
	}

	// Handle cdr
	switch args[1].Type() {
	case inter.NUMBER_OBJ:
		cdr = args[1].(*inter.NumberObject).Value
	case inter.BOOLEAN_OBJ:
		cdr = args[1].(*inter.BooleanObject).Value
	case inter.CHARACTER_OBJ:
		cdr = args[1].(*inter.CharacterObject).Value
	default:
		cdr = args[1]
	}

	return &inter.PairObject{Car: car, Cdr: cdr}
}

func Car(args []inter.Object, env *inter.Environment) inter.Object {
	if len(args) != 1 {
		return &Error{Message: "car requires exactly 1 argument"}
	}

	if args[0].Type() != inter.PAIR_OBJ {
		return &Error{Message: "car argument must be a pair"}
	}

	pairObj := args[0].(*inter.PairObject)

	car := pairObj.Car

	// Convert ds.Pair car value back to inter.Object
	return convertToObjObject(car)
}

func Cdr(args []inter.Object, env *inter.Environment) inter.Object {
	if len(args) != 1 {
		return &Error{Message: "cdr requires exactly 1 argument"}
	}

	if args[0].Type() != inter.PAIR_OBJ {
		return &Error{Message: "cdr argument must be a pair"}
	}

	pairObj := args[0].(*inter.PairObject)

	cdr := pairObj.Cdr

	// Convert cdr value back to inter.Object
	return convertToObjObject(cdr)
}

// SetCar modifies the car field of a pair
func SetCar(args []inter.Object, env *inter.Environment) inter.Object {
	if len(args) != 2 {
		return &Error{Message: "set-car! requires exactly 2 arguments"}
	}

	// First argument must be a pair
	pair, ok := args[0].(*inter.PairObject)
	if !ok {
		return &Error{Message: "set-car! first argument must be a pair"}
	}

	// Set the car field to the second argument
	pair.Car = args[1]

	// set-car! returns an unspecified value, we'll use NULL_OBJECT
	return inter.NULL_OBJECT
}

// SetCdr modifies the cdr field of a pair
func SetCdr(args []inter.Object, env *inter.Environment) inter.Object {
	if len(args) != 2 {
		return &Error{Message: "set-cdr! requires exactly 2 arguments"}
	}

	// First argument must be a pair
	pair, ok := args[0].(*inter.PairObject)
	if !ok {
		return &Error{Message: "set-cdr! first argument must be a pair"}
	}

	// Set the cdr field to the second argument
	pair.Cdr = args[1]

	// set-cdr! returns an unspecified value, we'll use NULL_OBJECT
	return inter.NULL_OBJECT
}

// Caar returns the car of the car of a pair
func Caar(args []inter.Object, env *inter.Environment) inter.Object {
	if len(args) != 1 {
		return &Error{Message: "caar requires exactly 1 argument"}
	}

	if args[0].Type() != inter.PAIR_OBJ {
		return &Error{Message: "caar argument must be a pair"}
	}

	pair := args[0].(*inter.PairObject)
	if pair.Car == nil {
		return &Error{Message: "caar: car of argument is nil"}
	}

	carPair, ok := pair.Car.(*inter.PairObject)
	if !ok {
		return &Error{Message: "caar: car of argument is not a pair"}
	}

	return convertToObjObject(carPair.Car)
}

// Cadr returns the car of the cdr of a pair
func Cadr(args []inter.Object, env *inter.Environment) inter.Object {
	if len(args) != 1 {
		return &Error{Message: "cadr requires exactly 1 argument"}
	}

	if args[0].Type() != inter.PAIR_OBJ {
		return &Error{Message: "cadr argument must be a pair"}
	}

	pair := args[0].(*inter.PairObject)
	if pair.Cdr == nil {
		return &Error{Message: "cadr: cdr of argument is nil"}
	}

	cdrPair, ok := pair.Cdr.(*inter.PairObject)
	if !ok {
		return &Error{Message: "cadr: cdr of argument is not a pair"}
	}

	return convertToObjObject(cdrPair.Car)
}

// Cdar returns the cdr of the car of a pair
func Cdar(args []inter.Object, env *inter.Environment) inter.Object {
	if len(args) != 1 {
		return &Error{Message: "cdar requires exactly 1 argument"}
	}

	if args[0].Type() != inter.PAIR_OBJ {
		return &Error{Message: "cdar argument must be a pair"}
	}

	pair := args[0].(*inter.PairObject)
	if pair.Car == nil {
		return &Error{Message: "cdar: car of argument is nil"}
	}

	carPair, ok := pair.Car.(*inter.PairObject)
	if !ok {
		return &Error{Message: "cdar: car of argument is not a pair"}
	}

	return convertToObjObject(carPair.Cdr)
}

// Cddr returns the cdr of the cdr of a pair
func Cddr(args []inter.Object, env *inter.Environment) inter.Object {
	if len(args) != 1 {
		return &Error{Message: "cddr requires exactly 1 argument"}
	}

	if args[0].Type() != inter.PAIR_OBJ {
		return &Error{Message: "cddr argument must be a pair"}
	}

	pair := args[0].(*inter.PairObject)
	if pair.Cdr == nil {
		return &Error{Message: "cddr: cdr of argument is nil"}
	}

	cdrPair, ok := pair.Cdr.(*inter.PairObject)
	if !ok {
		return &Error{Message: "cddr: cdr of argument is not a pair"}
	}

	return convertToObjObject(cdrPair.Cdr)
}
